Skip to content

Add unit tests to increase coverage for plugins and editor-adapter#3378

Merged
JiuqingSong merged 2 commits into
masterfrom
increase-test-coverage
Jun 24, 2026
Merged

Add unit tests to increase coverage for plugins and editor-adapter#3378
JiuqingSong merged 2 commits into
masterfrom
increase-test-coverage

Conversation

@JiuqingSong

@JiuqingSong JiuqingSong commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator

Summary

Add unit tests to cover code paths that previously had no test coverage, with no production code changes. A full Istanbul coverage pass confirmed these files/branches were either untested or had zero-hit paths; the additions close those gaps.

  • roosterjs-content-model-plugins
    • ContextMenuPluginBase — new test file (was untested): event handling, container init/reuse, onDismiss, dispose.
    • TouchPlugin — new test file (was untested): pointerDown detection timer, word-boundary snapping, doubleClick punctuation/space/word selection, dispose.
    • CustomReplacePlugin — added guard-branch coverage and actually exercised the formatTextSegmentBeforeSelectionMarker callback (match vs no-match, canUndoByBackspace).
    • TableEditPlugin — added onMouseMove / ensureTableRects / defaultTableSelector coverage (rect hit/miss, caching, editor forwarding).
    • TableEditor — added editing-lifecycle handlers (onFinishEditing, onStart*, onEnd*, onAfterInsert, onBeforeEditTable), public isEditing/isOwnedElement/onSelect, getOnMouseOut, and RTL coverage.
  • roosterjs-editor-adapter
    • EditorAdapter — new "API coverage" describe block for the public surface (addDomEventHandler, selection getters, getContent, setZoomScale, node manipulation, custom data, dom attributes, undo state, content-edit features, runAsync, etc.).
    • buildRangeEx and insertNode utils — new test files (were untested), covering every input/branch.

How to test

  1. Run the affected test suites:
    yarn test:fast --testPathPattern="ContextMenuPluginBase|TouchPlugin|CustomReplacePlugin|tableEditPluginTest|tableEditorTest|EditorAdapterTest|buildRangeExTest|insertNodeTest"
    
    All tests should pass.
  2. Optionally verify the whole suite and lint are still green:
    yarn test:fast
    yarn eslint
    

These are test-only changes, so there is no runtime behavior to verify manually.

Before:
image

After:
image

@github-actions

github-actions Bot commented Jun 19, 2026

Copy link
Copy Markdown
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://microsoft.github.io/roosterjs/pr-preview/pr-3378/

Built to branch gh-pages at 2026-06-24 21:37 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

Cover previously-untested code paths:
- ContextMenuPluginBase, TouchPlugin (new test files)
- CustomReplacePlugin guard branches and replacement callback
- TableEditPlugin onMouseMove/ensureTableRects/defaultTableSelector
- TableEditor editing lifecycle, public methods, getOnMouseOut, RTL
- EditorAdapter API surface (new describe block)
- buildRangeEx and insertNode utils (new test files)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@JiuqingSong JiuqingSong force-pushed the increase-test-coverage branch from aaa12c1 to eeeeb6b Compare June 19, 2026 19:40
@JiuqingSong JiuqingSong marked this pull request as ready for review June 19, 2026 19:46
@JiuqingSong JiuqingSong merged commit aa54077 into master Jun 24, 2026
8 checks passed
@JiuqingSong JiuqingSong deleted the increase-test-coverage branch June 24, 2026 21:35
juliaroldi added a commit that referenced this pull request Jun 26, 2026
* Add skipFormatContainerFallbackCheck to keep persisted Content Model path valid (#3358)

When formatInsertPointWithContentModel persists the Content Model group path during DOM to Model conversion, a FormatContainer that falls back to a plain paragraph would be removed from the model, leaving the persisted path invalid. Add a skipFormatContainerFallbackCheck option that keeps the FormatContainer in that scenario so the formatting callback can still rely on the path.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Clear whole list node stack when leaving its root parent (#3357)

When handleBlockGroupChildren finishes processing a block group whose list node stack is rooted at the parent being left, clear the entire node stack instead of only its inner levels so a sibling block group does not incorrectly reuse the invalidated list element.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Remove redundant leading/trailing selection markers within a paragraph (#3362)

When setSelection applies a range selection, a leading or trailing
SelectionMarker that has a real (non-marker) selected segment next to it in
the same paragraph carries no selection information and can be removed. Track,
per paragraph, whether any non-marker segment was selected and which boundary
markers were kept; after the segment loop, drop those boundary markers if the
paragraph contained real selected content.

This is scoped to the same paragraph on purpose: a marker at a paragraph edge
must be kept to distinguish "selection starts at the beginning of this line"
from "selection starts at the end of the previous line".

Update the #3341 reconcileSelection snapshots accordingly: the preserveMarker
guard still hands setSelection two live markers (so the selected segment keeps
isSelected), and setSelection now drops those redundant boundary markers.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* @ (#3360)

Reconcile IMG src and data-* attribute changes in Content Model cache

Previously any attribute change other than id on editor content would set
mutation type to "unknown" and invalidate the whole cached Content Model.

Recognize src and data-* changes on an indexed IMG element and update the
related ContentModelImage (src property or dataset) in place, so the cache
stays valid. Add a new "attribute" mutation type carrying the element and
attribute name, and a reconcileImageAttribute method on DomIndexer.


@

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Add Paste As Markdown support and isPastedContentMarkdown API (#3363)

Adds two new public APIs to roosterjs-content-model-markdown and a "Paste as Markdown" demo button that uses them to decide whether to convert clipboard content with convertMarkdownToContentModel or fall back to the regular paste flow.

* Recognize escaped characters when converting markdown to Content Model (#3365)

@
Treat a backslash-escaped ASCII punctuation character (e.g. "\*") as a
literal character so it is not parsed as a formatting marker.


@

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Add MarkdownPastePlugin to paste markdown content as rich text (#3366)

* Add MarkdownPastePlugin to convert pasted markdown content

Add a MarkdownPastePlugin in the roosterjs-content-model-markdown package
that handles the BeforePaste event and, when the clipboard content can be
interpreted as markdown, converts the plain text into a Content Model and
pastes it as rich content.

- Introduce the 'asMarkdown' PasteType so callers can explicitly request a
  markdown paste; the demo's "Paste as Markdown" button now routes through it.
- Add MarkdownPasteOptions with an autoConversion flag (default false) so the
  plugin can optionally auto-convert any markdown-looking paste.
- Map the new PasteType to Normal in the legacy editor adapter.
- Wire the plugin and its autoConversion option into the demo site's plugins
  side pane.

* Preserve new event pasteType when converting BeforePaste event (#3367)

This change makes the converter prefer the pasteType already present on the new event, only falling back to the mapping from the old event when the new event doesn't have one

* draft-pr (#3368)

Adds a new project skill, draft-pr, at .claude/skills/draft-pr/SKILL.md. The skill walks through creating a pull request for the current branch: it inspects the diff against master, generates a concise imperative title and a structured description (a ## Summary plus a ## How to test section), and opens the PR with the gh CLI. It follows the same authoring conventions as the existing version-bump skill and enforces repo-specific rules from AGENTS.md (PRs target master, tests use yarn test:fast).

* changeCapitalization API: Fix 'capitalize' making both halves of a word uppercase (#3372)

* new tests

* Only capitalize the beginning of a word

* Add button to demo

* Normalize line endings to LF for capitalization changes

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* skill (#3370)

* Bump dompurify from 3.4.0 to 3.4.9 (#3371)

Bumps [dompurify](https://github.com/cure53/DOMPurify) from 3.4.0 to 3.4.9.
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](cure53/DOMPurify@3.4.0...3.4.9)

---
updated-dependencies:
- dependency-name: dompurify
  dependency-version: 3.4.9
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jiuqing Song <jisong@microsoft.com>

* Bump dompurify from 3.4.9 to 3.4.11 (#3374)

Bumps [dompurify](https://github.com/cure53/DOMPurify) from 3.4.9 to 3.4.11.
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](cure53/DOMPurify@3.4.9...3.4.11)

---
updated-dependencies:
- dependency-name: dompurify
  dependency-version: 3.4.11
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump @babel/core from 7.11.1 to 7.29.7 (#3376)

Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.11.1 to 7.29.7.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.29.7/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-version: 7.29.7
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jiuqing Song <jisong@microsoft.com>

* create Image wrapper (#3373)

Fixes the editor jumping/scrolling when an image is selected for editing. In createImageWrapper.ts, attaching the shadow root replaces the rendered light-DOM image with a taller edit wrapper (border + resize/rotate/crop handles), which grows the surrounding line box and causes the browser to scroll the selection back into view.

This pins the shadow host span to the original image's footprint — captured from image.offsetWidth/offsetHeight before the shadow root takes over — by setting display: inline-block, fixed width/height, vertical-align: bottom, and overflow: visible. The handles and wrapper still overflow the host visually, but the line box no longer grows, so the editor stays put. Pinning is skipped when the image has no measurable footprint (offsetWidth/offsetHeight of 0).

* Re-enable and stabilize previously-disabled tests (#3377)

* Re-enable and stabilize previously-disabled tests

Several test suites were disabled (xit/xdescribe) due to flakiness or
environment dependence. Re-enable them and fix the underlying issues so
they pass deterministically regardless of spec order:

- selectionConverterTest: spy on the deep createRange module did not
  intercept the barrel import; exercise the real createRange with a
  document-attached image instead.
- updateRotateHandleTest: the spy targeted rotateHandle while the code
  reads rotateCenter; point it at the right element and feed each
  "hidden" case the intended edge distance (and correct an impossible
  expected handle value).
- applyChangeTest: replace brittle exact PNG data-URL comparisons with
  IHDR-dimension checks (encoder-independent), and create a fresh
  contentModelImage per test to remove order-dependent editingInfo state.
- tableEditorTest: give the editor a deterministic size so the table is
  never flush against the scroll-container edge, which otherwise made
  inserter visibility depend on leftover document.body layout.
- tableResizerTest: recreate the content model table per test (onDragging
  mutates it in place) and gate verifyCellRectChange on growth only so an
  incidental cross-axis border-box shift does not flip the assertion.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix test

* improve

* improve

* fix

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Fix normalizeSingleSegment to preserve leading space for word separation (#3379)

* Fix normalizeSingleSegment to preserve leading space for word separation

- Search backward for the nearest non-empty text segment to determine
  whether leading spaces should be preserved or removed
- Skip empty text segments during normalization to avoid corrupting context
- Replace nbsp with regular space for leading space preservation since
  nbsp prevents word wrapping
- Add unit tests for normalizeSingleSegment

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix comment

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add unit tests to increase coverage for plugins and editor-adapter (#3378)

Cover previously-untested code paths:
- ContextMenuPluginBase, TouchPlugin (new test files)
- CustomReplacePlugin guard branches and replacement callback
- TableEditPlugin onMouseMove/ensureTableRects/defaultTableSelector
- TableEditor editing lifecycle, public methods, getOnMouseOut, RTL
- EditorAdapter API surface (new describe block)
- buildRangeEx and insertNode utils (new test files)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Retain segments with undeletable links during model pruning (#3380)

* Retain segments with undeletable links during model pruning

Keep segments that have link.format.undeletable set to true in
pruneUnselectedModel, even when they are not selected. This prevents
undeletable link segments from being lost during selection-based pruning.

Add unit tests covering the new behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* improve

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* [Markdown Plugin] Support auto-conversion on native paste and add undoConversion option (#3381)

* version

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Jiuqing Song <jisong@microsoft.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Ian Elizondo <ianeli@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants